package com.googlecode.gwtresume.client.render;




import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;

import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ComplexPanel;
import com.google.gwt.user.client.ui.DockPanel;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasAlignment;
import com.google.gwt.user.client.ui.HasText;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.InlineHTML;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.client.ui.HasHorizontalAlignment.HorizontalAlignmentConstant;
import com.google.gwt.user.client.ui.HasVerticalAlignment.VerticalAlignmentConstant;
import com.googlecode.gwtresume.client.Renderer;
import com.googlecode.gwtresume.client.RendererOption;
import com.googlecode.gwtresume.client.api.Header;
import com.googlecode.gwtresume.client.api.Position;
import com.googlecode.gwtresume.client.api.Project;
import com.googlecode.gwtresume.client.api.Resume;
import com.googlecode.gwtresume.client.api.ResumeConstants;
import com.googlecode.gwtresume.client.api.Skill;
import com.googlecode.gwtresume.client.api.SkillSet;

public abstract class BaseRenderer implements Renderer {
	
	static final DateTimeFormat DF = DateTimeFormat.getFormat("MMMM yyyy");
	
	protected final DockPanel viewPort = new DockPanel();
	protected final VerticalPanel content = newVerticalPanel("content");
	
	protected Resume resume;
	protected boolean moreDetail;
	protected List<RendererOption> options;
	protected ResumeConstants constants;
	
	protected HorizontalAlignmentConstant halign = null;
	
	public BaseRenderer() {
		super();

	}
	
	abstract void render(VerticalPanel content);
	
	public Widget render(Resume resume, boolean moreDetail, List<RendererOption> options) {
		this.resume = resume;
		this.constants = resume.getConstants();
		this.moreDetail = moreDetail;
		this.options = options;
		
		render(content);

		viewPort.add(content, DockPanel.CENTER);
		viewPort.setCellHorizontalAlignment(content, HasAlignment.ALIGN_CENTER);
		viewPort.setWidth("100%");		

		return viewPort;
		
	}

	HorizontalPanel renderHeader() {
		HorizontalPanel header = newHorizontalPanel("header");
		VerticalPanel contact = newSectionPanel("contact");
		
		contact.add(newLabel("name", getFullName(), false));
		contact.add(newHorizontalPanel("address_line", newLabels("address", resume.getHeader().getAddress(), false)));
		contact.add(newHorizontalPanel("contact_line", newLabel("phone", formatPhone(resume.getHeader().getPhone()), false), getEmail()));
		contact.add(newHorizontalPanel("control_line", newHorizontalPanel("resume_controls", renderControls())));
		header.add(contact);

		return header;
	}
	
	VerticalPanel renderProfile() {
		VerticalPanel profile = newSectionPanel("profile", constants.profile());
		profile.add(newLabel("summary", resume.getSummary(), true));
		return profile;
	}
	
	Widget[] renderControls() {
		try {
			Widget[] controls = new Widget[options.size()];
			for (int i = 0; i < options.size(); i++) {
				final RendererOption opt = options.get(i);
				try {
					Widget[] links = opt.getOptions(true);
					if (links.length == 1) {
						links[0].setStylePrimaryName("resume_link");
						controls[i] = links[0];
					} else {
						OptionPopup p = new OptionPopup(opt.getLabel(), links);
						controls[i] = p.button;
					}
				} catch (Exception e) {

					throw e;
				}

			}

			return controls;
		} catch (Exception e) {
			e.printStackTrace();
			/* failsafe so as not to disrupt render */
			return new Widget[] {};
		}
	}
	
	
	
	class OptionPopup extends PopupPanel implements ClickHandler {
		
		final Button button;

	    public OptionPopup(String label, Widget...widgets) {
	      super(true);
	      this.button = new Button(label);
	      button.setStylePrimaryName("resume_controls");
	      this.button.addClickHandler(this);
	      setWidget(newVerticalPanel("option_popup", widgets));
	      for (Widget w : widgets) {
	    	  w.setStylePrimaryName("resume_link");
	    	  ((HasClickHandlers) w).addClickHandler(this);
	      }
	    }

		public void onClick(ClickEvent event) {
			if (event.getSource().equals(button)) {
				setPopupPosition(button.getAbsoluteLeft(), button.getAbsoluteTop() + button.getOffsetHeight());
				show();
			} else {
				hide();
			}
		}

	}

	VerticalPanel renderExperience() {
		VerticalPanel experience = newSectionPanel("experience", constants.experience());
		for (Position position : resume.getExperience()) {
			VerticalPanel pos = newVerticalPanel("position");
			
	
			pos.add(newHorizontalPanel("position_header", HasAlignment.ALIGN_BOTTOM, renderPositionHeader(position)));
			pos.add(newLabel("position_desc", position.getDescription(), true));
			for (Project project : position.getProjects()) {
				
				HTML projectName = html("project_name", project.getName() + " - ", true);
				HTML projectDesc = html("project_desc", project.getDescription(moreDetail), true);
				Tag pdetail = new Tag("p", "project_detail", projectName, projectDesc);
				pdetail.addStyleName("copy");
				pos.add(pdetail);
			}
			experience.add(pos);
		}
		return experience;
		
	}
	
	Label[] renderPositionHeader(Position position) {
				return new Label[] {
				newLabel("title", position.getTitle(), false),
				newLabel("employer", position.getEmployer(), false),
				newLabel("period", getPeriod(position), false)	
		};
	}


	
	VerticalPanel renderSkills() {
		VerticalPanel skills = newSectionPanel("skills", constants.skills(), halign);

		for (SkillSet skillSet : resume.getSkills()) {
			VerticalPanel set = newVerticalPanel("skill_set", halign);
			set.add(newLabel("skill_header", skillSet.getTitle(), false));				
			for (Skill skill : getSortedSkills(skillSet.getSkills())) {
				Label s = newLabel("skill_" + skill.getSkillLevel().toString(), skill.getName(), false);
				s.addStyleName("skill");
				set.add(s);
			}
			skills.add(set);
		}
		return skills;

	}
	
	VerticalPanel renderEducation() {
		
		VerticalPanel education = newSectionPanel("education", constants.education(), halign);
		for (String[] credit : resume.getEducation()) {
			VerticalPanel cr = newVerticalPanel("credit", halign);
			for (String html : credit) {
				cr.add(newLabel("credit_line", html, false));
			}
			education.add(cr);
			
		}

		return education;
	}
	
	
	
	HorizontalPanel renderFooter() {
		HorizontalPanel footer = newHorizontalPanel("footer");
		footer.add(newLabel("footer", constants.referencesMessage(), false));
	
		return footer;
	}
	
	
	
	
	
	
	protected String formatDate(Date date) {
		return DF.format(date);
	}
	
	protected String getFullName() {
		return resume.getHeader().getFirstName() + " " + resume.getHeader().getLastName();
	}
	
	protected String formatPhone(String phone) {
		if (phone.matches(constants.phonePattern())) return phone.replaceAll(constants.phonePattern(), constants.phoneFormat());
		else return phone;
	}

	public String getPeriod(Position p) {
		return formatDate(p.getStartDate()) + " - " + (p.isPresentPosition() ? constants.present() : formatDate(p.getEndDate()));
		
	}
	
	protected Anchor getEmail() {
		Header h = resume.getHeader();
		Anchor a = new Anchor(h.getEmail(), "mailto:" + h.getEmail());
		a.setStylePrimaryName("email");
		return a;
	}
	
	protected List<Skill> getSortedSkills(List<Skill> skills) {
		List<Skill> list = new ArrayList<Skill>(skills);
		Collections.sort(list, new Comparator<Skill>() {
			public int compare(Skill o1, Skill o2) {
				return o1.getSkillLevel().compareTo(o2.getSkillLevel());
			}
		});
		return list;
	}
	
	
	
	
	
	
	
	
	
	
	
	
	
	protected VerticalPanel newVerticalPanel(String style, Widget...widgets) {
		return newVerticalPanel(style, null, widgets);
	}
	
	
	protected VerticalPanel newVerticalPanel(String style, HorizontalAlignmentConstant align, Widget...widgets) {
		VerticalPanel content = new VerticalPanel();
		content.setStylePrimaryName(style);
		if (align != null) content.setHorizontalAlignment(align);
		for (Widget w : widgets) content.add(w);
		return content;
	}
	
	protected HorizontalPanel newHorizontalPanel(String style, Widget...widgets) {
		return newHorizontalPanel(style, null, widgets);
	}
	protected HorizontalPanel newHorizontalPanel(String style, VerticalAlignmentConstant align, Widget...widgets) {
		HorizontalPanel content = new HorizontalPanel();
		if (align != null) content.setVerticalAlignment(align);
		content.setStylePrimaryName(style);
		for (Widget w : widgets) content.add(w);
		return content;
	}
	
	protected VerticalPanel newSectionPanel(String style, String header) {
		return newSectionPanel(style, header, null);
	}
	
	protected VerticalPanel newSectionPanel(String style) {
		return newSectionPanel(style, null, null);
	}	
	
	protected VerticalPanel newSectionPanel(String style, String header, HorizontalAlignmentConstant align) {
		VerticalPanel p = new VerticalPanel();
		p.setStylePrimaryName(style);
		if (align != null) p.setHorizontalAlignment(align);
		p.addStyleName("section");
		if (header != null) p.add(newSectionHeader(header));		
		return p;
	}
	
	protected Label newSectionHeader(String header) {
		return newLabel("section_header", header, false);
	}
	
	public static FlowPanel newPanel(String id) {
		FlowPanel panel = new FlowPanel();
		DOM.setElementAttribute(panel.getElement(), "id", id);
		return panel;
	}
	public static FlowPanel newPanel(String id, Panel parent) {
		FlowPanel p = newPanel(id);
		parent.add(p);
		return p;
	}
	
	
	
	
	protected Label newLabel(String style, String text, boolean wrap) {
		Label label = new Label(text, wrap);
		label.setStylePrimaryName(style);
		label.addStyleName(wrap? "copy" : "label");
		return label;
	}
	
	
	protected Label[] newLabels(String style, String[] text, boolean wrap) {
		Label[] labels = new Label[text.length];
		for (int i = 0; i < text.length; i++) labels[i] = newLabel(style, text[i], wrap);
		return labels;
	}
	
	protected HTML html(String style, String content, boolean inline) {
		HTML html = inline ? new InlineHTML(content) : new HTML(content);
		html.setStylePrimaryName(style);
		return html;
	}
	public static String concat(String delimiter, String... strings) {
		StringBuffer sb = new StringBuffer();
		for (String s : strings) {
			if (sb.length() > 0) sb.append(delimiter);
			sb.append(s);
		}
		return sb.toString();
	}
	
	public static class Tag extends ComplexPanel implements HasText {
		
		
		public Tag(String tag) {
			setElement(DOM.createElement(tag));
		}
		
		public Tag(String tag, String text) {
			this(tag);
			setText(text);
		}

		public Tag(String tag, Widget...widgets) {
			this(tag);
			for (Widget widget : widgets) add(widget);
		}
		
		public Tag(String tag, String style, String text) {
			this(tag, text);
			setStylePrimaryName(style);			
		}

		public Tag(String tag, String style, Widget... widgets) {
			this(tag, widgets);
			setStylePrimaryName(style);
			
		}

		public void add(Widget w) {
			super.add(w, getElement());
		}

		public void insert(Widget w, int beforeIndex) {
			super.insert(w, getElement(), beforeIndex, true);
		}

		public String getText() {
			return DOM.getInnerText(getElement());
		}

		public void setText(String text) {
			DOM.setInnerText(getElement(), (text == null) ? "" : text);
		}
		
		

	}

}
